#pragma once

namespace MyUtils
{

namespace DataType
{

namespace Units
{

template < int M, int K, int S > struct MKSUnit
{
    enum { meter = M, kilogram = K, second = S };
};

template < typename MKSUnit > class Value
{
  private:
    long double m_value;

  public:
    constexpr explicit Value( long double value = 0.0L ) noexcept
        : m_value( value ){ };
    ~Value( ) = default;

    constexpr long double value( ) const noexcept { return m_value; };

    constexpr operator long double( ) const noexcept { return m_value; };

    constexpr Value &operator=( const Value &rhs ) noexcept
    {
        this->m_value = rhs.value( );
        return *this;
    };
};

template < int M, int K, int S >
constexpr Value< MKSUnit< M, K, S > >
operator+( const Value< MKSUnit< M, K, S > > &lhs,
           const Value< MKSUnit< M, K, S > > &rhs ) noexcept
{
    return Value< MKSUnit< M, K, S > >( lhs.value( ) + rhs.value( ) );
}

template < int M, int K, int S >
constexpr Value< MKSUnit< M, K, S > >
operator-( const Value< MKSUnit< M, K, S > > &lhs,
           const Value< MKSUnit< M, K, S > > &rhs ) noexcept
{
    return Value< MKSUnit< M, K, S > >( lhs.value( ) - rhs.value( ) );
}

template < int M1, int K1, int S1, int M2, int K2, int S2 >
constexpr Value< MKSUnit< M1 + M2, K1 + K2, S1 + S2 > >
operator*( const Value< MKSUnit< M1, K1, S1 > > &lhs,
           const Value< MKSUnit< M2, K2, S2 > > &rhs ) noexcept
{
    return Value< MKSUnit< M1 + M2, K1 + K2, S1 + S2 > >( lhs.value( ) *
                                                          rhs.value( ) );
}

template < int M1, int K1, int S1, int M2, int K2, int S2 >
constexpr Value< MKSUnit< M1 - M2, K1 - K2, S1 - S2 > >
operator/( const Value< MKSUnit< M1, K1, S1 > > &lhs,
           const Value< MKSUnit< M2, K2, S2 > > &rhs ) noexcept
{
    return Value< MKSUnit< M1 - M2, K1 - K2, S1 - S2 > >( lhs.value( ) /
                                                          rhs.value( ) );
}

template < int M, int K, int S >
constexpr bool operator==( const Value< MKSUnit< M, K, S > > &lhs,
                           const Value< MKSUnit< M, K, S > > &rhs ) noexcept
{
    return lhs.value( ) == rhs.value( );
}

template < int M, int K, int S >
constexpr bool operator!=( const Value< MKSUnit< M, K, S > > &lhs,
                           const Value< MKSUnit< M, K, S > > &rhs ) noexcept
{
    return lhs.value( ) != rhs.value( );
}

//由于涉及到矢量之间的比较，因此用户需要自行设计<和>运算符

using DimensionlessQuantity = Value< MKSUnit< 0, 0, 0 > >;
using Length = Value< MKSUnit< 1, 0, 0 > >;
using Area = Value< MKSUnit< 2, 0, 0 > >;
using Volume = Value< MKSUnit< 3, 0, 0 > >;

using Mass = Value< MKSUnit< 0, 1, 0 > >;
using Time = Value< MKSUnit< 0, 0, 1 > >;
using Speed = Value< MKSUnit< 1, 0, -1 > >;
using Acceleration = Value< MKSUnit< 1, 0, -2 > >;
using Force = Value< MKSUnit< 1, 1, -2 > >;
using Pressure = Value< MKSUnit< -1, 1, -2 > >;

using Momentum = Value< MKSUnit< 1, 1, -1 > >;

constexpr Length operator"" _m( long double value ) noexcept
{
    return Length( value );
}

constexpr Area operator"" _m2( long double value ) noexcept
{
    return Area( value );
}

constexpr Volume operator"" _m3( long double value ) noexcept
{
    return Volume( value );
}

constexpr Mass operator"" _kg( long double value ) noexcept
{
    return Mass( value );
}

constexpr Time operator"" _s( long double value ) noexcept
{
    return Time( value );
}

constexpr Speed operator"" _m_s_1( long double value ) noexcept
{
    return Speed( value );
}

constexpr Acceleration operator"" _m_s_2( long double value ) noexcept
{
    return Acceleration( value );
}

constexpr Force operator"" _N( long double value ) noexcept
{
    return Force( value );
}

constexpr Momentum operator"" _N_s( long double value ) noexcept
{
    return Momentum( value );
}

} // namespace Units
} // namespace DataType

} // namespace MyUtils
